// UTF-8 w/o BOM

#include <tadbg.h>
#include <res_def.h>
#include <func.h>
#include <DAL.h>
#include <dl_keypad.h>
#include "elf.h"
#include "gdi.h"
#include "font.h"
//#include <.h>
//#include ".h"

////////////////////////////////////////////////////////////////////////////////

static	UINT32	AppStart(EVENT_STACK_T* ev_st, REG_ID_T reg_id, UINT32 param2);
static	UINT32	AppExit(EVENT_STACK_T* ev_st, APPLICATION_T* app);

//static	UINT32	MainStateEnter(EVENT_STACK_T *ev_st, APPLICATION_T *app, ENTER_STATE_TYPE_T type);
//static	UINT32	MainStateExit(EVENT_STACK_T *ev_st, APPLICATION_T *app, EXIT_STATE_TYPE_T type);

static	UINT32	HandleKeyPress(EVENT_STACK_T* ev_st, APPLICATION_T* app);
static	UINT32	HandleKeyRelease(EVENT_STACK_T* ev_st, APPLICATION_T* app);
static	UINT32	HandleExit(EVENT_STACK_T* ev_st, APPLICATION_T* app);
static	UINT32	HandleTimerExpiried(EVENT_STACK_T* ev_st, APPLICATION_T* app);
//static	UINT32	Handle( EVENT_STACK_T *ev_st,  APPLICATION_T *app );

static void displayCbk(const RECT_T* updatedRect);

////////////////////////////////////////////////////////////////////////////////

//static void RenderBackground( void );
static void Render( void );
static void Physics( void );

static void NewGame( INT32 lvl );

////////////////////////////////////////////////////////////////////////////////

#define	TIMER_ID		0xD10D
#define	TIMER_TIMEOUT	25
#define INACT_ID		0xDEAD
#define INACT_TIMEOUT	1000

#define MUL				10000
#define WND_W			176
#define WND_H			220
#define PONG_H			4
#define PONG_W			32
#define BALL_W			6
#define PONGS			2
#define PLACEMENT		2

#define	ELF_NAME		"Pong"

// Название приложения. Длина строки именно такая и никакая иначе
const char		app_name[APP_NAME_LEN] = ELF_NAME; 

RECT_T		cbkRect = {0, 0, 176, 220};

UINT32		evcode_base;
ldrElf		elf;

UINT32		hTimer, hInTimer;

BOOL		idle = false;

AHIDEVCONTEXT_T	hDC;
AHISURFACE_T	hDisp, hDraw;

////////////////////////////////////////////////////////////////////////////////

UINT32			cBg   = ATI_565RGB(0, 0, 0);
UINT32			cFg   = ATI_565RGB(255, 255, 255);
UINT32			cTx   = ATI_565RGB(192, 192, 192);
UINT32			cCopy = ATI_565RGB(128, 128, 192);

BOOL buttons[3] = {false, false, false};

AHIRECT_T rc[3];

AHIPOINT_T pongs[PONGS] = {
    {MUL*WND_W/2, MUL*(WND_H - PONG_H - PLACEMENT)},
    {MUL*WND_W/2, MUL*(PLACEMENT)}
};
AHIPOINT_T ball = { MUL*(WND_W-BALL_W)/2, MUL*(WND_H-BALL_W)/2 };

INT32 movement[2] = { MUL*1.2, MUL*1.2 };

INT32 speedX = MUL*1, speedY = MUL*1, speedM = MUL*6, accel = MUL*0.0003, pongAccel = MUL*0.1;
INT32 count[2] = {0, 0};
INT32 level = 1, max = 10;

BOOL playing = false, gameover = false;

UINT8 lastkey = KEY_MULTIKEYS_PRESSED;

////////////////////////////////////////////////////////////////////////////////


// Обработчики событий для каждого state-а

// Обработчики событий для APP_STATE_ANY (используется в любом state)
static EVENT_HANDLER_ENTRY_T any_state_handlers[] =
{
	{ EV_KEY_PRESS,			HandleKeyPress		},
	{ EV_KEY_RELEASE,		HandleKeyRelease	},
	{ EV_PM_API_EXIT,		HandleExit			},		// PM API: cmd EXIT
	
	// Список всегда должен заканчиватся такой записью
	{ STATE_HANDLERS_END,	NULL				}
};


static EVENT_HANDLER_ENTRY_T main_state_handlers[] =
{
	{ EV_TIMER_EXPIRED,		HandleTimerExpiried },
	
	// Список всегда должен заканчиватся такой записью
	{ STATE_HANDLERS_END,	NULL				}
};


////////////////////////////////////////////////////////////////////////////////


// Таблица соответствий обработчиков, состояний и функций входа-выхода из состояния.
// Порядок состояний такой же, как в enum-e
static const STATE_HANDLERS_ENTRY_T state_handling_table[] =
{
	{ APP_STATE_ANY,		// State
		NULL,				// Обработчик входа в state
		NULL,				// Обработчик выхода из state
		any_state_handlers	// Список обработчиков событий
	},

	{ APP_STATE_MAIN,
		NULL,				//MainStateEnter,
		NULL,				//MainStateExit,
		main_state_handlers
	}

};





// Это EntryPoint для всех эльфов, должна быть объявлена именно так
// uri - путь к эльфу (аналогично argv[0])
// params - параметры эльфа (аналогично остальным argv)
ldrElf* _start( WCHAR* uri, WCHAR* params ) {
	
	UINT32				status = RESULT_OK;
	UINT32				reserve;
	UINT8				i;
	
	// Чтоб компилер не ругался
	uri = uri;
	params = params;
	
	// Проверяем, а вдруг уже запущено?
	if (ldrIsLoaded((char*)app_name)) {
		cprint("\x87" ELF_NAME ":\x8c Already Loaded!\n");
		return NULL; // Нам не нужен второй экземпляр
	}
	
	evcode_base = ldrRequestEventBase();
	elf.evbase = evcode_base;
	
	// инитим таблицы ивентов
	reserve = evcode_base + 1;
	for (i = 0; i < sizeof(state_handling_table)/sizeof(STATE_HANDLERS_ENTRY_T); i++ ) {
		reserve = ldrInitEventHandlersTbl( (EVENT_HANDLER_ENTRY_T *)state_handling_table[i].htable, reserve );
	}
	
	// Регистрируем приложение
	status = APP_Register(	&evcode_base,			// На какое событие должно запускаться приложение
							1,						// Кол-во событий, на которое должно запускаться приложение
							state_handling_table,	// Таблица состояний
							APP_STATE_MAX,			// Количество состояний
							(void*)AppStart );		// Функция, вызываемая при запуске
	
	dbgf("Register: status = %d", status);
	
	elf.name = (char*)app_name;
	ldrSendEvent(evcode_base); // Запустить немедленно
	
	return &elf;
}





// Функция вызываемая при старте приложения
UINT32 AppStart(EVENT_STACK_T* ev_st,  REG_ID_T reg_id,  UINT32 param2) {
	
	APPLICATION_T		*app = NULL;
	UINT32				status = RESULT_OK;
	
	dbg("AppStart: Enter");
	
	// Инициализация для фоновых приложений
	app = (APPLICATION_T*)APP_InitAppData(	(void*)APP_HandleEventPrepost,	// Обработчик для фоновых приложений
											sizeof(APPLICATION_T),			// Размер структуры приложения
											reg_id,
											0, 0,
											1,
											AFW_APP_CENTRICITY_NONE,
											AFW_PREPROCESSING,
											AFW_POSITION_TOP );

	dbgf("AppStart: APP_InitAppData 0x%X", app);
	
	if ( !app )	{
		cprint("\x87" ELF_NAME ":\x8c Init Failed!\n");
		return ldrUnloadElf();
	}
	
	status = APP_Start( ev_st, app,
						APP_STATE_MAIN,			// Начальное состояние для фонового приложения
						state_handling_table,
						AppExit,
						app_name,
						0 );

	dbgf("AppStart: APP_Start, status = 0x%X", status);
	
	if ( status == RESULT_OK ) {
		elf.app = app;
		GDI_Init(&hDC, &hDisp, &hDraw, app_name, GDI_PRIVATEDC, AHIROP_PATCOPY, AHIROP_PATCOPY, false);	// Инициализация драйвера
		ldrDisplayCbkReg((dispCbk)displayCbk, &cbkRect, TRUE);										// Регистрация коллбека
		hTimer = StartTimer(TIMER_TIMEOUT, TIMER_ID, ttCyclical, app);								// Старт таймера
		hInTimer = StartTimer(INACT_TIMEOUT, INACT_ID, ttCyclical, app);
		//RenderBackground();
		NewGame(level);
	} else {
		cprint("\x87" ELF_NAME ":\x8c Start Failed!\n");
		APP_HandleFailedAppStart( ev_st, app, 0 );
		return ldrUnloadElf(); 
	}
	
	return RESULT_OK;
}





// Функция выхода из приложения
UINT32 AppExit( EVENT_STACK_T* ev_st,  APPLICATION_T* app ) {
	
	UINT32				status;

	StopTimer(&hTimer, app);					// Остановка таймера #1
	StopTimer(&hInTimer, app);					// Остановка таймера #2
	ldrDisplayCbkUnReg((dispCbk)displayCbk);	// Удаление коллбека
	GDI_UnInit(&hDC);							// Деинициализация драйвера
	
	cprint("\x87" ELF_NAME ":\x83 Exit\n");

	// Завершаем работу приложения
	status = APP_ExitStateAndApp(ev_st, app, 0);

	// Выгружаем эльф
	return ldrUnloadElf(); 
}





// Обработчик события, EV_KEY_PRESS в данном случае
UINT32 HandleKeyPress( EVENT_STACK_T* ev_st,  APPLICATION_T* app ) {
	// Получаем код кнопки
	UINT8	key = GET_KEY(ev_st);
	lastkey = key;
	
	// Делаем что-нибудь
	APP_ConsumeEv(ev_st, app);
	
	switch (key) {
		case KEY_RED:
			app->exit_status = TRUE;
			break;
		case KEY_JOY_OK:
		case KEY_5:
			playing = !playing;
			break;
		case KEY_LEFT:
		case KEY_4:
			buttons[0] = true; buttons[2] = false;
			break;
		case KEY_RIGHT:
		case KEY_6:
			buttons[0] = false; buttons[2] = true;
			break;
	}

	return RESULT_OK;
}





// Обработчик события, EV_KEY_PRESS в данном случае
UINT32 HandleKeyRelease(EVENT_STACK_T* ev_st,  APPLICATION_T* app) {
	// Получаем код кнопки
	UINT8	key = GET_KEY(ev_st);
	
	// Делаем что-нибудь
	if (lastkey != KEY_MULTIKEYS_PRESSED) {
		APP_ConsumeEv(ev_st, app);
	}
	
	switch (key) {
		case KEY_LEFT:
		case KEY_4:
			buttons[0] = false;
			break;
		case KEY_RIGHT:
		case KEY_6:
			buttons[2] = false;
			break;
		case KEY_0:
			if (playing) {
				level = 1;
				NewGame(level);
			}
			//RenderBackground();
			break;
	}

	return RESULT_OK;
}





// Функция обработки события таймера
UINT32 HandleTimerExpiried(EVENT_STACK_T* ev_st, APPLICATION_T* app) {
	// Получаем идентификатор таймера
	UINT32	tid = GET_TIMER_ID(ev_st);
	
	switch (tid) {
		case TIMER_ID:
			// Делаем что-нибудь
			APP_ConsumeEv(ev_st, app);
			if ((!gameover)&&(playing)) { Physics(); }
			if ((!playing)||(gameover)) {
				if (!idle)	{
					Render();
					idle = true;
				}
			} else {
				if (idle) { idle = false;	}
				Render();
			}
			break;
		case INACT_ID:
			ldrSendEvent(ldrGetConstVal(EV_DISPLAY_ACTIVE));
			break;
	}

	return RESULT_OK;
}





// PM API: Exit - выход из приложения через диспетчер задач
UINT32 HandleExit(EVENT_STACK_T* ev_st,  APPLICATION_T* app) {
	
	APP_ConsumeEv( ev_st, app );
	app->exit_status = TRUE;
	
	return RESULT_OK;
}





void displayCbk(const RECT_T* updatedRect) {
	Render();
	// Рисуем поверх UIS
}




void Render( void ) {
	char    buffer[32];
	UINT32 	len;
	
	GDI_BeginPaint(&hDC, &hDisp, &hDraw);
	
	GDI_FillRect(&hDC, 0, 0, 176, 220, cBg);
	
	if (playing) {
		if (!gameover) {
			sprintf(buffer, "%i-%i", count[0], count[1]);
			len = getStringWidth(buffer, 1, 2, 0);
			drawString(hDC, (UINT32)(WND_W/2 - len/2), (UINT32)(WND_H/2 - 8), buffer, cTx, cBg, 1, 2, 0);
		
			sprintf(buffer, "LEVEL %i", level);
			len = getStringWidth(buffer, 1, 1, 0);
			drawString(hDC, (UINT32)(WND_W/2 - len/2), (UINT32)(WND_H/2 + 12), buffer, cTx, cBg, 1, 1, 0);
		
			rc[0].x1 = (UINT32)(pongs[0].x/MUL - PONG_W/2);
			rc[0].y1 = (UINT32)pongs[0].y/MUL;
			rc[0].x2 = (UINT32)rc[0].x1 + PONG_W;
			rc[0].y2 = (UINT32)rc[0].y1 + PONG_H;
		
			rc[1].x1 = (UINT32)(pongs[1].x/MUL - PONG_W/2);
			rc[1].y1 = (UINT32)pongs[1].y/MUL;
			rc[1].x2 = (UINT32)rc[1].x1 + PONG_W;
			rc[1].y2 = (UINT32)rc[1].y1 + PONG_H;
		
			rc[2].x1 = (UINT32)(ball.x/MUL - BALL_W/2);
			rc[2].y1 = (UINT32)(ball.y/MUL - BALL_W/2);
			rc[2].x2 = (UINT32)rc[2].x1 + BALL_W;
			rc[2].y2 = (UINT32)rc[2].y1 + BALL_W;
		
			AhiDrawBrushFgColorSet(hDC, cFg);
			AhiDrawSpans(hDC, rc, 3, AHIFLAG_LINE_STRIP);
		
			if (count[1] > max) {
				level++;
				NewGame(level);
			}
		
			if (count[0] > max) {
				gameover = true;
				//RenderBackground();
			}
			
		} else {
			sprintf(buffer, "GAME OVER!", level);
			len = getStringWidth(buffer, 1, 2, 0);
			drawString(hDC, (UINT32)(WND_W/2 - len/2), (UINT32)(WND_H/2 + 12), buffer, cTx, cBg, 1, 2, 0);
			sprintf(buffer, "© 2010 Cool-T & Motofan.Ru");
			len = getStringWidth(buffer, 1, 1, 0);
			drawString(hDC, (UINT32)(WND_W/2 - len/2), (UINT32)(WND_H - 32), buffer, cTx, cBg, 1, 1, 0);
		}
	} else {
		sprintf(buffer, "PAUSED", level);
		len = getStringWidth(buffer, 1, 2, 0);
		drawString(hDC, (UINT32)(WND_W/2 - len/2), (UINT32)(WND_H/2 + 12), buffer, cTx, cBg, 1, 2, 0);
		sprintf(buffer, "© 2010 Cool-T & Motofan.Ru");
		len = getStringWidth(buffer, 1, 1, 0);
		drawString(hDC, (UINT32)(WND_W/2 - len/2), (UINT32)(WND_H - 32), buffer, cTx, cBg, 1, 1, 0);
	}
	
	GDI_EndPaint(&hDC, &hDisp, &hDraw);
	
}




void NewGame( INT32 lvl ) {
	playing = false;
	gameover = false;
	ball.x = MUL*(WND_W-BALL_W)/2;
	ball.y = MUL*(WND_H-BALL_W)/2;
	pongs[0].x = MUL*WND_W/2;
	pongs[0].y = MUL*(WND_H - PONG_H - PLACEMENT);
    pongs[1].x = MUL*WND_W/2;
	pongs[1].y = MUL*(PLACEMENT);
	speedX = MUL*1;
	speedY = MUL*1;
	count[0] = 0;
	count[1] = 0;
	movement[0] = MUL*1;
	movement[1] = MUL*1;
	//RenderBackground();
	playing = true;
}





void Physics( void ) {
	char    buffer[32];
	UINT32 	len;
	
	if (speedX > 0) { speedX += accel; } else { speedX -= accel; }
	if (speedY > 0) { speedY += accel; } else { speedY -= accel; }

	// Max speed
	if (speedX > speedM) { speedX = speedM; }
	if (speedX < -speedM) { speedX = -speedM; }
	if (speedY > speedM) { speedY = speedM; }
	if (speedY < -speedM) { speedY = -speedM; }

	// Ball "reflection"
	if (ball.x/MUL > WND_W - BALL_W) { speedX = -abs(speedX); }
	if (ball.x/MUL < BALL_W) {  speedX = abs(speedX); }

	if (ball.y/MUL > WND_H - BALL_W/2) { speedY = -abs(speedY); count[0]++; movement[0] += pongAccel; }
	if (ball.y/MUL < BALL_W/2) { speedY = abs(speedY); count[1]++; movement[1] += pongAccel; }

	// Movement of pong
	if (buttons[0]) { if (pongs[0].x/MUL > PONG_W/2) { pongs[0].x -= movement[0]; } }
	if (buttons[2]) { if (pongs[0].x/MUL < (WND_W-PONG_W/2)) { pongs[0].x += movement[0]; } }

	// Movement of ball
	if ((ball.y/MUL > WND_H - PONG_H - BALL_W/2 - PLACEMENT)&&(ball.x/MUL > pongs[0].x/MUL - PONG_W/2)&&(ball.x/MUL < pongs[0].x/MUL + PONG_W/2)) {
		speedY = -abs(speedY);
		speedX = 4*(ball.x - pongs[0].x)/PONG_W;
	}

	if ((ball.y/MUL < PONG_H + BALL_W/2 + PLACEMENT)&&(ball.x/MUL > pongs[1].x/MUL - PONG_W/2)&&(ball.x/MUL < pongs[1].x/MUL + PONG_W/2)) {
		speedY = abs(speedY);
		speedX = 4*(ball.x - pongs[1].x)/PONG_W;
	}

	ball.x += speedX;
	ball.y += speedY;

	// Bot (second player)
	if (ball.x/MUL > pongs[1].x/MUL + PONG_W/level) { if (pongs[1].x/MUL < (WND_W-PONG_W/2)) { pongs[1].x += movement[1]; } }
	if (ball.x/MUL < pongs[1].x/MUL - PONG_W/level) { if (pongs[1].x/MUL > PONG_W/2) { pongs[1].x -= movement[1]; } }
	
}


// Конец файла. GAME OVER!